Fork me on GitHub

Redis 高可用实践

Sentinel 简介

如上图所示,Redis 高可用是通过Sentinel来实现的,是Redis官方推荐的高可用性(HA)解决方案,Sentinel英文含义是哨兵,放哨的,可以理解为它是Redis集群的监控者,监控着所有的masterslave机器的健康状况。首先我们来看下它提供了哪些功能。

1.监控(Monitoring):
监控所有主从机的健康状态,当主从机连接出现问题时,会自动变更有问题机器的状态。
2.通知(Notification):
一旦集群的节点有问题,Sentinel可以通过API来通知系统管理员或者其他的应用程序。
3.自动执行故障转移(Automatic failover):
如果某个主节点挂掉,Sentinel会自动踢掉这个主节点,并且从它的从节点中选举一个出来做主节点,其他的从节点也会把这个从节点作为主节点。而且Sentinel还会持续监控挂掉的主节点,一旦发现活过来了,再次把它加入集群中的一个从节点。
4.作为一个配置提供者(Configuration provider):
Sentinel充当客户端服务发现的权威来源:客户端连接到Sentinels以请求负责给定服务的当前Redis主服务器的地址。 如果发生故障转移,Sentinels将报告新地址。

Sentinel是如何实现的?

Sentinel并不是一个新东西,它本质上只是一个运行在特殊模式下的Redis服务器,怎么个特殊法?比如它不支持很多的键值命令,因为它不是用来做存储的,它是用来管理集群的啊。Sentinel最核心的工作时记录集群节点的信息,比如每个节点的健康状态,以及某个节点故障后,剔除掉坏的机器。

Sentinel 支持哪些集群模式

Sentinel可以监视任意多个主服务器以及这些主服务下的从服务器,因此在集群中你可以一主多从,多主多从。
像这样:

Sentinel也支持多个Sentinel集群,几个Sentinel共同监控一个集群,像这样:

此时,多个Sentinel会进行相互通信来进行管理集群,比如要对故障节点进行下线,那必须经过多个(可配置)Sentinel同意才能执行下线,或者选举新的主服务器时,多个Sentinel也要相互协商。

多个Sentinel是官方推荐的,因为单个Sentinel也会有单点故障的问题,万一这个Sentinel挂掉了咋办?

上手Sentinel

安装

Sentinel是随redis安装的,无需再进行额外安装。如果你已经安装了Redis,可以看下Sentinel的版本

1
redis-sentinel -v

配置

Sentinel包含了一个名为sentinel.conf的配置文件,运行一个 Sentinel 所需的最少配置如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
//监视哪个主服务器,2代表执行故障时执行故障转移至少要两个` sentinel'`同意。
sentinel monitor mymaster 127.0.0.1 6379 2
//指定了 Sentinel 主观认为服务器已经断线所需的毫秒数。
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
//
sentinel parallel-syncs mymaster 1

//..继续增加,监视多个服务器
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

failover-timeout: sentinel集群都遵守一个规则:如果sentinel A推荐sentinel B去执行failover,B会等待一段时间后,自行再次去对同一个master执行failover,这个等待的时间是通过failover-timeout配置项去配置的。从这个规则可以看出,sentinel集群中的sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的sentinel如果失败了,另外一个将会在一定时间内进行重新进行failover,以此类推。
parallel-syncs:在发生failover主备切换时,这个选项指定了最多可以有多少个slave同时对新的master进行同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。

启动

启动命令如下,配置文件参数是强制性的,必须要,因为sentinel会使用配置文件保存当前状态,在运行中,配置文件会根据系统状态动态更改,启动时也会重新加载。

1
redis-sentinel  /usr/local/etc/redis/sentinel.conf

另外,sentinel默认使用的端口是26379,可以使用redis客户端进行连接,如果是本地安装的话,命令如下:

1
redis-cli -h 127.0.0.1 -p 26379

Sentinel API

使用 Docker-compose 搭建 Sentinel 集群

借助 Docker-compose ,我们 可以轻松的搭建一个集群环境,docker-compose.yml配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
version: '3'
services:
redis-master1:
image: redis
container_name: redis-master1
volumes:
- "/Users/jarviszhao/docker/redis/conf/:/usr/local/etc/redis/"
ports:
- "6382:6379"
command: redis-server /usr/local/etc/redis/redis-master1.conf

redis-slave1:
image: redis
container_name: redis-slave1
volumes:
- "/Users/jarviszhao/docker/redis/conf:/usr/local/etc/redis/"
ports:
- "6380:6379"
command: redis-server /usr/local/etc/redis/redis-slave1.conf --slaveof redis-master1 6379
links:
- redis-master1

redis-slave2:
image: redis
container_name: redis-slave2
volumes:
- "/Users/jarviszhao/docker/redis/conf:/usr/local/etc/redis/"
ports:
- "6381:6379"
command: redis-server /usr/local/etc/redis/redis-slave2.conf --slaveof redis-master1 6379
links:
- redis-master1

redis-sentinel1:
image: redis
container_name: redis-sentinel1
volumes:
- "/Users/jarviszhao/docker/redis/conf:/usr/local/etc/redis/"
ports:
- "26383:26379"
command: redis-sentinel /usr/local/etc/redis/sentinel1.conf
links:
- redis-master1

redis-sentinel2:
image: redis
container_name: redis-sentinel2
volumes:
- "/Users/jarviszhao/docker/redis/conf:/usr/local/etc/redis/"
ports:
- "26384:26379"
command: redis-sentinel /usr/local/etc/redis/sentinel2.conf
links:
- redis-master1

redis-sentinel3:
image: redis
container_name: redis-sentinel3
volumes:
- "/Users/jarviszhao/docker/redis/conf:/usr/local/etc/redis/"
ports:
- "26385:26379"
command: redis-sentinel /usr/local/etc/redis/sentinel3.conf
links:
- redis-master1

集群中包含三个Sentinel,一个master和两个salve。可以使用docker pause命令来中断master的进程,观察是否执行了故障转移。代码见Github 仓库

Sentinel 详细内部机制

主观下线和客观下线

主观下线
sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了(subjectively down, 也简称为SDOWN)。而配置项down-after-milliseconds就是用来指定这个“一定时间范围”的,单位是毫秒。

客观下线
sentinel会向集群中的其他sentinel询问故障的master的状态,当也认为master已经下线的sentinel超过配置数量时,sentinel会认为该master当前状态是客观下线(ODOWN)。ODOWN需要一定数量的sentinel达成一致意见才能认为一个master客观上已经宕掉,各个sentinel之间通过命令SENTINEL is_master_down_by_addr来获得其它sentinel对master的检测结果。
如果之后master可用了,这个状态就会相应地被清理掉。
ODOWN状态只适用于master,对于不是master的redis节点sentinel之间不需要任何协商,slaves和sentinel不会有ODOWN状态。

Sentinel 与 master ,salve 之间是如何通信的?

Sentinelmastersalve之间有两个异步网络连接。

  1. 一是命令连接,专门向服务器发送命令,并接受命令回复。
  2. 二是订阅连接,专门订阅主服务的__sentinel_hello__频道。

Sentinel会以每10秒一次的频率向主从服务器发送info命令,来获取主从服务器的信息,以及新加入集群其他的sentinel。Sentinel内部保存着其他Sentinel的信息,如果其他Sentinel有更新,此时保存的信息也会更新。

如何执行失效转移?

当一个主服务被判断为客观下线时,监视这个下线服务器的各个Sentinel会进行协商,选举出个领头Sentinel来执行失效转移。使用 Raft 算法来选出领头Sentinel。确保在一个给定的纪元(epoch)里, 只有一个领头产生。
关于raft 算法(也是zookeeper的核心算法)
http://www.jdon.com/artichect/raft.html

参考

01.Redis Sentinel 官方文档
02.Redis主从集群的Sentinel配置
03.使用Docker Compose部署基于Sentinel的高可用Redis集群
04.Redis 高可用(1)——Sentinel 篇
Redis Sentinel机制与用法(一)